#include "../EPoller.h"
#include "../Channel.h"
using namespace yhaida;

static_assert(EPOLLIN == POLLIN);
static_assert(EPOLLPRI == POLLPRI);
static_assert(EPOLLOUT == POLLOUT);
static_assert(EPOLLRDHUP == POLLRDHUP);
static_assert(EPOLLERR == POLLERR);
static_assert(EPOLLHUP == POLLHUP);

namespace
{
    const int kNew = -1;
    const int kAdded = 1;
    const int kDeleted = 2;
}
EPoller::EPoller(EventLoop *loop)
    : _mainEventLoop(loop),
      _epollfd(::epoll_create1(EPOLL_CLOEXEC)),
      _events(kInitEventListSize),
      _channels()
{
    if (_epollfd < 0)
    {
        LOG_SYSFATAL << "EPoller::EPoller";
    }
}
EPoller::~EPoller()
{
    ::close(_epollfd);
}
Timestamp EPoller::poll(ChannelList *EventLoopActiveChannels, int timeout)
{
    int num = ::epoll_wait(_epollfd, &*_events.begin(), _events.size(), timeout);
    Timestamp nowTime(Timestamp::now());

    if (num > 0)
    {
        LOG_TRACE << "events happended";
        fillActiveChannels(num, EventLoopActiveChannels);

        //扩容
        if (static_cast<size_t>(num) == _events.size())
        {
            _events.reserve(_events.size() * 2);
        }
    }
    else if (num == 0)
    {
        LOG_TRACE << "nothing happended";
    }
    else
    {
        LOG_SYSERR << "EPoller::poll()";
    }
    return nowTime;
}
void EPoller::update(int operation, Channel *channel)
{
    struct epoll_event event;
    bzero(&event, sizeof(event));
    event.data.ptr = channel;
    event.events = channel->events();
    int fd = channel->fd();
    if (::epoll_ctl(_epollfd, operation, fd, &event) < 0)
    {
        if (operation == EPOLL_CTL_DEL)
        {
            LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd;
        }
        else
        {
            LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd;
        }
    }
}

void EPoller::updataChannel(Channel *channel)
{
    assertInLoopThread();
    LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
    const int index = channel->index();
    if (index == kNew || index == kDeleted) // channel->fd()不在_pollfds中
    {
        // a new one, add with EPOLL_CTL_ADD
        int fd = channel->fd();

        if (index == kNew)
        {
            assert(_channels.find(fd) == _channels.end()); // new
            _channels[fd] = channel;
        }
        else // index == kDeleted
        {
            assert(_channels.find(fd) != _channels.end()); // 已经deleted
            assert(_channels[fd] == channel);
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel);
    }
    //更新的不是fd(因为map里面存的是Channel*不用更新啦),是_pollfds里面events
    else
    {
        int fd = channel->fd();
        (void)fd;
        assert(_channels.find(fd) != _channels.end());
        assert(_channels[fd] == channel);
        assert(index == kAdded);

        if (channel->isNoneEvent()) // delete
        {
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted);
        }
        else
        {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}
/*
typedef union epoll_data {
    void *ptr;//存Channel类
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

struct epoll_event
{
  uint32_t events;
  epoll_data_t data;
}

*/
void EPoller::fillActiveChannels(int numEvents, ChannelList *EventLoopActiveChannels) const
{
    assert(static_cast<size_t>(numEvents) <= _events.size());

    // for (EventList::const_iterator pfd = _events.begin();
    //      pfd != _events.end();
    //      ++pfd)
    for (int i = 0; i < numEvents; ++i)
    {
        Channel *channel = static_cast<Channel *>(_events[i].data.ptr);

#ifndef NDEBUG
        int fd = channel->fd();
        ChannelMap::const_iterator it = _channels.find(fd);
        assert(it != _channels.end());
        assert(it->second == channel);
#endif
        channel->setRevents(_events[i].events);
        EventLoopActiveChannels->push_back(channel);
    }
}
//删除EventsList _epollfd中和ChannelMap _channels
void EPoller::removeChannel(Channel *channel)
{
    assertInLoopThread();
    int fd = channel->fd();
    LOG_TRACE << "fd = " << fd;

    assert(_channels.find(fd) != _channels.end());
    assert(_channels[fd] == channel);
    assert(channel->isNoneEvent()); //检验channel->disenableAll()

    int index = channel->index();

    assert(index == kAdded || index == kDeleted);

    size_t n = _channels.erase(fd);
    assert(n == 1); //唯一
    (void)n;

    if (index == kAdded)
    {
        update(EPOLL_CTL_DEL, channel);
    }
    channel->set_index(kNew);
}